package controllers

import java.io.{File, FileFilter}
import java.nio.file.Files

import actor.MissionManageActor
import akka.actor.{Actor, ActorSystem, PoisonPill, Props}
import akka.stream.Materializer
import command.{CommandExec, MissionUtils}
import dao._
import javax.inject.Inject
import org.apache.commons.io.FileUtils
import play.api.libs.json.{JsArray, JsNumber, JsString, JsValue, Json}
import play.api.mvc.{AbstractController, Action, ControllerComponents, WebSocket}
import tool.{FormTool, Tool}
import utils._

import scala.jdk.CollectionConverters._
import models.Tables._
import implicits.Implicits._
import org.joda.time.DateTime
import org.zeroturnaround.zip.ZipUtil
import play.api.data.Form
import play.api.data.Forms.{mapping, seq, text}
import play.api.libs.streams.ActorFlow
import shapeless.Poly1
import shared.VarTool
import tool.Pojo.{CommandData, OrthoMclData}

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.language.postfixOps
import scala.util.{Failure, Success}


/**
 * Created by Administrator on 2020/1/17
 */
class GenomeController @Inject()(cc: ControllerComponents, userDao: UserDao, sampleDao: SampleDao,
                                 geneInfoDao: GeneInfoDao, classifyDao: ClassifyDao, formTool: FormTool
                                )(implicit val system: ActorSystem,
                                  implicit val tool: Tool,
                                  implicit val missionDao: MissionDao,
                                  implicit val modeDao: ModeDao,
                                  implicit val materializer: Materializer) extends
  AbstractController(cc) {

  val missionManageActor = system.actorOf(
    Props(new MissionManageActor())
  )
  missionManageActor ! "ask"

  def toCrisprHelp = Action { implicit request =>
    Ok(views.html.user.crisprHelp())
  }

  def crisprBefore = Action { implicit request =>
    Ok(views.html.user.crispr())
  }

  def crisprResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "out/result.json")
    val jsonStr = FileUtils.readFileToString(file)
    val json = Json.parse(jsonStr)
    Ok(json)
  }

  def vfdbAnnoBefore = Action { implicit request =>
    Ok(views.html.user.vfdbAnno())
  }

  def vfdbResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "vfdb.anno.xls")
    val (columnNames, array) = Utils.getInfoByFile(file)
    val json = Json.obj("columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }

  def vfdbAnno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.vfdbForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val seqType = if (data.seqType == "p") "protein" else "nucleic acid"
    val args = List(
      s"Evalue:${data.evalue}",
      s"Max target seqs:${data.maxTargetSeqs}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.vfdbStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def ardbAnnoBefore = Action { implicit request =>
    Ok(views.html.user.ardbAnno())
  }

  def ardbResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "ardb.anno.xls")
    val (columnNames, array) = Utils.getInfoByFile(file)
    val json = Json.obj("columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }

  def ardbAnno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.ardbForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val args = List(
      s"Evalue:${data.evalue}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.ardbStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def cazyAnnoBefore = Action { implicit request =>
    Ok(views.html.user.cazyAnno())
  }

  def cazyResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "cazy.anno.xls")
    val (columnNames, array) = Utils.getInfoByFile(file)
    val json = Json.obj("columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }

  def cazyAnno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.cazyForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val args = List(
      "无"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.cazyStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def annoBefore = Action { implicit request =>
    Ok(views.html.user.anno())
  }

  def annoResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "annotation.table.xls")
    val (columnNames, array) = Utils.getInfoByFile(file)
    val json = Json.obj("columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }


  def anno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.annoForm.bindFromRequest().get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val args = if (data.annoMethods.contains("kegg")) {
      List(
        s"Evalue:${data.eValue}",
        s"Max target seqs:${data.maxTargetSeqs}"
      ).mkString(";")
    } else {
      ""
    }
    val row = MissionRow(0, s"${missionName}", userId, VarTool.annoStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def getExampleContent = Action { implicit request =>
    val data = formTool.fileNameForm.bindFromRequest().get
    val file = new File(Tool.exampleDir, s"${data.fileName}")
    val str = FileUtils.readFileToString(file, "UTF-8")
    Ok(Json.toJson(str))
  }

  def tcdbAnno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.vfdbForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val seqType = if (data.seqType == "p") "protein" else "nucleic acid"
    val args = List(
      s"Evalue:${data.evalue}",
      s"Max target seqs:${data.maxTargetSeqs}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.tcdbStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def pfamAnno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.pfamForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val args = List(
      s"Evalue sequence cutoff:${data.seqCutoff}",
      s"Evalue domain cutoff:${data.domainCutoff}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.pfamStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def assemble = Action.async(parse.multipartFormData) { implicit request =>
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val fq1File = Tool.fileMoveDir("fq1File", tmpDir)
    val fq2File = Tool.fileMoveDir("fq2File", tmpDir)
    val userId = tool.getUserId
    val args = List(
      s"fastq1 file:${fq1File.getName}",
      s"fastq2 file:${fq2File.getName}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.assembleStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val gzDir = Tool.getGzDir(workspaceDir)
      val useFq1File = fq1File.fileCopyToDir(gzDir)
      val useFq2File = fq2File.fileCopyToDir(gzDir)
      val json = Json.obj("fq1" -> useFq1File.getAbsolutePath, "fq2" -> useFq2File.getAbsolutePath)
      json.toString().toFile(Tool.getConfigFile(workspaceDir))
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Tool.deleteDirectory(tmpDir)
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def assemblePredict = Action.async(parse.multipartFormData) { implicit request =>
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val data = FormTool.shapeForm.bindFromRequest.get
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val fq1File = Tool.fileMoveDir("fq1File", tmpDir)
    val fq2File = Tool.fileMoveDir("fq2File", tmpDir)
    val userId = tool.getUserId
    val args = List(
      s"fastq1 file:${fq1File.getName}",
      s"fastq2 file:${fq2File.getName}",
      s"Prokaryote DNA shape:${data.shape}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.assemblePredictStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val gzDir = Tool.getGzDir(workspaceDir)
      val useFq1File = fq1File.fileCopyToDir(gzDir)
      val useFq2File = fq2File.fileCopyToDir(gzDir)
      val json = Json.obj("fq1" -> useFq1File.getAbsolutePath, "fq2" -> useFq2File.getAbsolutePath,
        "shape" -> data.shape)
      json.toString().toFile(Tool.getConfigFile(workspaceDir))
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Tool.deleteDirectory(tmpDir)
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def predict = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.predictForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val args = List(
      s"Prokaryote DNA shape:${data.shape}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.predictStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def tcdbAnnoBefore = Action { implicit request =>
    Ok(views.html.user.tcdbAnno())
  }

  def pfamAnnoBefore = Action { implicit request =>
    Ok(views.html.user.pfamAnno())
  }

  def assembleBefore = Action { implicit request =>
    Ok(views.html.user.assemble())
  }

  def predictBefore = Action { implicit request =>
    Ok(views.html.user.predict())
  }

  def tcdbResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "tcdb.anno.xls")
    val (columnNames, array) = Utils.getInfoByFile(file)
    val json = Json.obj("columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }

  def pfamResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = Tool.getPfamOutFile(resultDir)
    val json = Utils.getTxtFileJsonNoLower(file)
    Ok(json)
  }

  def phiAnnoBefore = Action { implicit request =>
    Ok(views.html.user.phiAnno())
  }

  def phiResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "phi.anno.xls")
    val (columnNames, array) = Utils.getInfoByFile(file)
    val json = Json.obj("columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }

  def phiAnno = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.vfdbForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val seqType = if (data.seqType == "p") "protein" else "nucleic acid"
    val args = List(
      s"Evalue:${data.evalue}",
      s"Max target seqs:${data.maxTargetSeqs}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.phiStr, args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def updateMissionSocket(kind: String) = WebSocket.accept[JsValue, JsValue] {
    implicit request =>
      val userId = Tool.getUserId
      case class MissionAction(beforeMissions: Seq[MissionRow], action: String)
      ActorFlow.actorRef(out => Props(new Actor {
        override def receive: Receive = {
          case msg: JsValue if (msg \ "info").as[String] == "start" =>
            val beforeMissions = Utils.execFuture(missionDao.selectAll(userId, kind))
            out ! Utils.getJsonByTs(beforeMissions)
            system.scheduler.scheduleOnce(3 seconds, self, MissionAction(beforeMissions, "update"))
          case MissionAction(beforeMissions, action) =>
            missionDao.selectAll(userId, kind).map {
              missions =>
                val currentMissions = missions
                if (currentMissions.size != beforeMissions.size) {
                  out ! Utils.getJsonByTs(currentMissions)
                } else {
                  val b = currentMissions.zip(beforeMissions).forall {
                    case (currentMission, beforeMission) =>
                      currentMission.id == beforeMission.id && currentMission.state == beforeMission.state
                  }
                  if (!b) {
                    out ! Utils.getJsonByTs(currentMissions)
                  }
                }
                system.scheduler.scheduleOnce(3 seconds, self, MissionAction(currentMissions, "update"))
            }
          case _ =>
            self ! PoisonPill
        }

        override def postStop(): Unit = {
          self ! PoisonPill
        }
      }))
  }

  def crispr = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.crisprForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val basicArgs = List(
      s"Minimal Repeat Length:${data.minDR}",
      s"Maximal Repeat Length:${data.maxDR}",
      s"Allow Repeat Mismatch:${if (data.arm.isDefined) "yes" else "no"}",
      s"Minimal Spacers size in function of Repeat size:${data.percSPmin}",
      s"Maximal Spacers size in function of Repeat size:${data.percSPmax}",
      s"Maximal allowed percentage of similarity between Spacers:${data.spSim}",
      s"Percentage mismatchs allowed between Repeats:${data.mismDRs}",
      s"Percentage mismatchs allowed for truncated Repeat:${data.truncDR}",
      s"Size of Flanking regions in base pairs (bp) for each analyzed CRISPR array:${data.flank}",
      s"Alternative detection of truncated repeat:${if (data.dt.isDefined) "yes" else "no"}",
      s"Perform CAS gene detection:${if (data.cas.isDefined) "yes" else "no"}"
    )
    val defValue = data.defValue match {
      case "S" => "SubTyping"
      case "T" => "Typing"
      case "G" => "General (permissive)"
    }
    val extraArgs = if (data.cas.isDefined) {
      List(
        s"Clustering model:${defValue}",
        s"Unordered:${if (data.meta.isDefined) "yes" else "no"}"
      )
    } else List[String]()
    val argStr = (basicArgs ::: extraArgs).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, VarTool.crisprStr, argStr, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val seqFile = Tool.getSeqFile(workspaceDir)
      data.method match {
        case "text" =>
          data.queryText.toFile(seqFile)
        case "file" =>
          val file = request.body.file("file").get
          file.ref.moveFileTo(seqFile, replace = true)
      }
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def getAllMission = Action.async {
    implicit request =>
      val data = formTool.kindForm.bindFromRequest().get
      val kind = data.kind
      val userId = tool.getUserId
      missionDao.selectAll(userId, kind).map {
        x =>
          val array = Utils.getArrayByTs(x)
          Ok(Json.toJson(array))
      }
  }

  def missionNameCheck = Action.async { implicit request =>
    val data = formTool.missionForm.bindFromRequest.get
    val userId = tool.getUserId
    missionDao.selectOptionByMissionAttr(userId, data.missionName, data.kind).map { mission =>
      mission match {
        case Some(y) => Ok(Json.obj("valid" -> false))
        case None =>
          Ok(Json.obj("valid" -> true))
      }
    }
  }

  def downloadResult = Action.async {
    implicit request =>
      val userId = tool.getUserId
      val data = formTool.missionIdForm.bindFromRequest().get
      val missionId = data.missionId
      missionDao.selectByMissionId(userId, missionId).map {
        mission =>
          val missionIdDir = tool.getMissionIdDirById(missionId)
          val resultDir = new File(missionIdDir, "result")
          val resultFile = new File(missionIdDir, s"result.zip")
          if (!resultFile.exists()) ZipUtil.pack(resultDir, resultFile)
          Ok.sendFile(resultFile).withHeaders(
            CACHE_CONTROL -> "max-age=3600",
            CONTENT_DISPOSITION -> s"attachment; filename=${
              mission.missionName
            }.zip",
            CONTENT_TYPE -> "application/x-download"
          )
      }
  }

  def downloadResultFile = Action.async {
    implicit request =>
      val userId = tool.getUserId
      val data = formTool.missionFileForm.bindFromRequest().get
      val missionId = data.missionId
      missionDao.selectByMissionId(userId, missionId).map {
        mission =>
          val missionIdDir = tool.getMissionIdDirById(missionId)
          val resultDir = new File(missionIdDir, "result")
          val file = new File(resultDir, data.fileName)
          Ok.sendFile(file).withHeaders(
            //            CACHE_CONTROL -> "max-age=3600",
            CONTENT_DISPOSITION -> s"attachment; filename=${
              file.getName
            }",
            CONTENT_TYPE -> "application/x-download"
          )
      }
  }

  def getLogContent = Action.async {
    implicit request =>
      val userId = tool.getUserId
      val data = formTool.missionIdForm.bindFromRequest().get
      missionDao.selectByMissionId(userId, data.missionId).map {
        mission =>
          val missionIdDir = tool.getMissionIdDirById(data.missionId)
          val logFile = new File(missionIdDir, s"log.txt")
          val logStr = FileUtils.readFileToString(logFile, "UTF-8")
          Ok(Json.toJson(logStr))
      }
  }

  def deleteMissionById = Action.async {
    implicit request =>
      val data = formTool.deleteMissionForm.bindFromRequest().get
      missionDao.deleteById(data.missionId).map {
        x =>
          val missionIdDir = tool.getMissionIdDirById(data.missionId)
          Utils.deleteDirectory(missionIdDir)
          Redirect(routes.GenomeController.getAllMission() + "?kind=" + data.kind)
      }
  }

  def phyTreeBefore = Action { implicit request =>
    Ok(views.html.user.phyTree())
  }

  def phyTreeResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val treeFile = new File(resultDir, "tree.newick")
    val treeStr = FileUtils.readFileToString(treeFile)
    Ok(Json.obj("tree" -> treeStr))
  }

  def phyTree = Action.async(parse.multipartFormData) { implicit request =>
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val sampleFiles = Tool.fileMoveDirs("sampleFiles", tmpDir)
    val sampleNames = sampleFiles.map(_.namePrefixH)
    val refSampleFile = Tool.fileMoveDir("refSampleFile", tmpDir)
    val refSampleName = refSampleFile.namePrefixH
    if (sampleNames.contains(refSampleName)) {
      Utils.result2Future(Ok(Json.obj("valid" -> false, "message" -> "样品文件名不能包含参考样品文件名!")))
    } else {
      Tool.changeFaFilesSuffix(refSampleFile :: sampleFiles, tmpDir)
      val argStr = List(
        s"Reference sample:${refSampleName}",
        s"Sample:${sampleNames.mkString("<br>")}"
      ).mkString(";")
      val row = MissionRow(0, s"${missionName}", userId, VarTool.phyTreeStr, argStr, new DateTime(), None, "running")
      missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
        val outDir = Tool.getUserMissionDir
        val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
        val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
        tmpDir.listFiles().filter(x => x.getName.endsWith(".genome.fa")).
          foreach(file => file.fileCopyToDir(workspaceDir))
        Tool.deleteDirectory(tmpDir)
        val configFile = Tool.getConfigFile(workspaceDir)
        val json = Json.obj("refSampleName" -> refSampleName, "sampleNames" -> sampleNames)
        Json.stringify(json).toFile(configFile)
        val newMission = mission.copy(state = "wait")
        missionDao.update(newMission).map { x =>
          Ok(Json.obj("valid" -> true))
        }
      }
    }
  }

  def orthoMclBefore = Action { implicit request =>
    Ok(views.html.user.orthoMcl())
  }

  def mummerBefore = Action { implicit request =>
    Ok(views.html.user.mummer())
  }

  def mummerResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file1 = new File(resultDir, "Target-Query.parallel.png")
    val base641 = Utils.getBase64Str(file1)
    val file2 = new File(resultDir, "Target-Query.xoy.png")
    val base642 = Utils.getBase64Str(file2)
    val coverFile = new File(resultDir, "cover.txt")
    val (coverColumnNames, coverArray) = Utils.getInfoByFile(coverFile)
    val json = Json.obj("base641" -> base641, "base642" -> base642,
      "cover" -> Json.obj("columnNames" -> coverColumnNames, "array" -> coverArray))
    Ok(json)
  }

  def getImage = Action.async { implicit request =>
    val ifModifiedSinceStr = request.headers.get(IF_MODIFIED_SINCE)
    val userId = tool.getUserId
    val data = formTool.missionFileForm.bindFromRequest().get
    val missionId = data.missionId
    missionDao.selectByMissionId(userId, missionId).map { mission =>
      val missionIdDir = tool.getMissionIdDirById(missionId)
      val resultDir = new File(missionIdDir, "result")
      val file = new File(resultDir, data.fileName)
      val lastModifiedStr = file.lastModified().toString
      val MimeType = "image/png"
      val byteArray = Files.readAllBytes(file.toPath)
      if (ifModifiedSinceStr.isDefined && ifModifiedSinceStr.get == lastModifiedStr) {
        NotModified
      }
      else {
        Ok(byteArray).as(MimeType).withHeaders(LAST_MODIFIED -> file.lastModified().toString)
      }
    }
  }

  def getFileContent = Action.async { implicit request =>
    val userId = tool.getUserId
    val data = formTool.missionFileForm.bindFromRequest().get
    val missionId = data.missionId
    missionDao.selectByMissionId(userId, missionId).map { mission =>
      val missionIdDir = tool.getMissionIdDirById(missionId)
      val resultDir = new File(missionIdDir, "result")
      val file = new File(resultDir, data.fileName)
      val byteArray = FileUtils.readFileToString(file)
      //      val byteArray = Files.readAllBytes(file.toPath)
      Ok(Json.toJson(byteArray))
    }
  }

  def mummer = Action.async(parse.multipartFormData) { implicit request =>
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val querySampleFile = Tool.fileMoveDir("querySampleFile", tmpDir)
    val refSampleFile = Tool.fileMoveDir("refSampleFile", tmpDir)
    val querySampleName = querySampleFile.namePrefixH
    val refSampleName = refSampleFile.namePrefixH
    List(querySampleFile, refSampleFile).foreach { file =>
      val destFile = new File(tmpDir, s"${file.getName.fileNamePrefixH}.genome.fa")
      if (!destFile.exists()) file.copyTo(destFile)
    }
    val args = List(
      s"Query(查询样本):${querySampleName}",
      s"Target(目标样本):${refSampleName}"
    ).mkString(";")
    if (querySampleName == refSampleName) {
      Future.successful(Ok(Json.obj("valid" -> false, "message" -> "查询样本文件名和目标样本文件文件名不能相同！")))
    } else {
      val row = MissionRow(0, s"${missionName}", userId, "mummer", args, new DateTime(), None, "running")
      missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
        val outDir = Tool.getUserMissionDir
        val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
        val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
        tmpDir.listFiles().filter(x => List("fa","fasta").exists(y=>x.getName.endsWith(s".${y}"))).
          foreach(file => file.fileCopyToDir(workspaceDir))
        Tool.deleteDirectory(tmpDir)
        val configFile = Tool.getConfigFile(workspaceDir)
        val json = Json.obj("querySampleName" -> querySampleName, "refSampleName" -> refSampleName)
        Json.stringify(json).toFile(configFile)
        val newMission = mission.copy(state = "wait")
        missionDao.update(newMission).map { x =>
          Ok(Json.obj("valid" -> true))
        }
      }
    }
  }

  def toMummerHelp = Action { implicit request =>
    Ok(views.html.user.mummerHelp())
  }

  def orthoMclResult = Action { implicit request =>
    val data = formTool.missionIdForm.bindFromRequest().get
    val resultDir = tool.getResultDirById(data.missionId)
    val file = new File(resultDir, "all_orthomcl.out")
    val lines = FileUtils.readLines(file).asScala
    val allData = lines.map { line =>
      val columns = line.split("\t")
      val orthoMcls = columns(0)
      val orthoMcl = columns(0).replaceAll("\\(.*\\):", "")
      val genesR = ".*\\((\\d+) genes.*".r
      val genesR(genes) = orthoMcls
      val taxaR = ".*,(\\d+) taxa.*".r
      val taxaR(taxa) = orthoMcls
      Json.obj("orthomcl" -> orthoMcl, "genes" -> genes, "taxa" -> taxa, "detail" -> columns(1))
    }
    val infoFile = new File(resultDir, "gene.family.info.xls")
    val infoLines = FileUtils.readLines(infoFile).asScala.drop(1)
    val infoData = infoLines.map { line =>
      val columns = line.split("\t")
      Json.obj("Sam" -> columns(0), "homology.gene.family" -> columns(1), "special_gene" -> columns(2), "all" -> columns(3))
    }
    val strainFile = new File(resultDir, "genefamily.strain.mat.xls")
    val (columnNames, array) = Utils.getInfoByFile(strainFile)
    val json = Json.obj("allData" -> allData, "infoData" -> infoData, "columnNames" -> columnNames, "array" -> array)
    Ok(json)
  }

  def orthoMcl = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.orthoMclForm.bindFromRequest.get
    val missionName = formTool.missionNameForm.bindFromRequest().get.missionName
    val userId = tool.getUserId
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val files = Tool.fileMoveDirs("sampleFile", tmpDir)
    val sampleNames = files.map { file =>
      file.namePrefixH
    }
    Tool.changeFaFilesSuffix(files, tmpDir)
    val args = List(
      s"样品(多选):${sampleNames.mkString("<br>")}",
      s"Pv Cutoff:${data.pvCutoff}",
      s"Pi Cutoff:${data.piCutoff}",
      s"Pmatch Cutoff:${data.pmatchCutoff}"
    ).mkString(";")
    val row = MissionRow(0, s"${missionName}", userId, "orthoMcl", args, new DateTime(), None, "running")
    missionDao.insert(row).flatMap(_ => missionDao.selectByMissionName(row.userId, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      tmpDir.listFiles().filter(x => x.getName.endsWith(".genome.fa")).
        foreach(file => file.fileCopyToDir(workspaceDir))
      Tool.deleteDirectory(tmpDir)
      val configFile = Tool.getConfigFile(workspaceDir)
      val json = Utils.getJsonByT(data)
      Json.stringify(json).toFile(configFile)
      val listFile = new File(workspaceDir, "fa.list")
      val listBuffer = sampleNames.map { sampleName =>
        s"${sampleName}\t${sampleName}.fa\t${sampleName}"
      }
      listBuffer.toFile(listFile)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def assemblePredictBefore = Action { implicit request =>
    Ok(views.html.user.assemblePredict())
  }


}
